#include "OSCthulhuClient.h"
#include "stdio.h"
#include "ClientPacketListener.h"
#include "Constants.h"
#include "QWriteLocker"
#include "QReadLocker"
#include "QTime"
#include "typeinfo"
#include <QDebug>
#include "gui/MainWindow.h"
#include "ui_MainWindow.h"

OSCthulhuClient::OSCthulhuClient(QString serverAddress, QString serverPassword, QString userName, QString userPassword, int serverPort, int clientPort, int appPort, QSemaphore *wait4CleanupDone,QSettings* settings) :
    settings(settings)
{
    connected = false;
    objectPrinting = false;
    oscPrinting = false;
    mw.show();
    mw.setUserName( userName );
    mw.setClient( this );
    this->serverAddress = serverAddress;
    this->serverPassword = serverPassword;
    this->userName = userName;
    this->userPassword = userPassword;
    this->serverPort = serverPort;
    this->clientPort = clientPort;
    this->appPort = appPort;
    appPorts.append(appPort);
    cat = new ClientAliveTimer();
    this->wait4CleanupDone = wait4CleanupDone;
    //this->appSocket = new UdpTransmitSocket(IpEndpointName("127.0.0.1",appPort));
    qDebug() <<"OSCthulhu Client started. ";
    qDebug() <<"Server Address = " << qPrintable(this->serverAddress);
    qDebug() <<"Server port = " << this->serverPort;
    qDebug() <<"Client Port = " << this->clientPort;
    qDebug() <<"External App port = " << this->appPort;
    oscThread = new ClientOSCThread(this);
    QObject::connect(this,SIGNAL(peerAdded(QString)),&mw,SLOT(addPeer(QString)));
    QObject::connect(this,SIGNAL(peerRemoved(QString)),&mw,SLOT(removePeer(QString)));
    QObject::connect(this,SIGNAL(syncObjectAdded(QString)),&mw,SLOT(addSyncObject(QString)));
    QObject::connect(this,SIGNAL(syncObjectRemoved(QString,QString,QString)),&mw,SLOT(removeSyncObject(QString,QString,QString)));
    //QObject::connect(&mw,SIGNAL(flush()),this,SLOT(flush()));
    QObject::connect((mw.getFlushButton()),SIGNAL(clicked()),this,SLOT(flush()));
    QObject::connect((mw.getFlushButton()),SIGNAL(pressed()),this,SLOT(flush()));

    QObject::connect(&mw, SIGNAL(setObjectPrint(bool)), this, SLOT(setObjectPrinting(bool)));
    QObject::connect(this, SIGNAL(printObject(QString,QStringList)), &mw, SLOT(printObject(QString,QStringList)));
    QObject::connect(this, SIGNAL(removePrintObject(QString)), &mw, SLOT(removePrintObject(QString)));
    QObject::connect(&mw, SIGNAL(setOSCPrint(bool)), this, SLOT(setOSCPrinting(bool)));
    QObject::connect(this, SIGNAL(printOSC(QString)), &mw, SLOT(printOSC(QString)));
    // mw.getUI()->PortList->insertItem(0,QString().setNum(appPort));
    mw.addPort(appPort);
    QObject::connect(this,SIGNAL(peerAdded(QString)),this,SLOT(sendAddPeer(QString)));
    QObject::connect(this,SIGNAL(peerRemoved(QString)),this,SLOT(sendRemovePeer(QString)));
    QObject::connect(this,SIGNAL(syncObjectAdded(QString)),this,SLOT(sendSyncObject(QString)));
    QObject::connect(this,SIGNAL(syncObjectRemoved(QString,QString,QString)),this,SLOT(sendRemoveSyncObject(QString,QString,QString)));

    QObject::connect(this,SIGNAL(syncArgChanged(QString,int)),this,SLOT(sendSyncArg(QString,int)));

    QObject::connect(this,SIGNAL(localSyncArgChanged(QString,int,int)),this,SLOT(localSendSyncArg(QString,int,int)));

    QObject::connect(cat,SIGNAL(timeOut()),this,SLOT(increaseAliveTimer()));

    QObject::connect(this,SIGNAL(connectedGUI()),&mw,SLOT(connected()));
    QObject::connect(this,SIGNAL(disconnectedGUI()),&mw,SLOT(disconnected()));
    QObject::connect(this,SIGNAL(changeGUIPiece(QString,QString)),&mw,SLOT(changeUserPiece(QString,QString)));

    loginThread = new LoginThread(this);
}

void OSCthulhuClient::sendServer(osc::OutboundPacketStream p) {
    try {
        this->oscThread->send(
                this->serverAddress,
                this->serverPort,
                p
        );
    } catch(...) {
        qCritical() << "An error occurred while attempting to send a message to the Server.";
    }
}

void OSCthulhuClient::sendServerSlot(osc::OutboundPacketStream p) {
    sendServer(p);
    qDebug() << "Sending to server";
}

/* simple multicast chat to everyone else.
IMPORTANT: Anytime you want to multicast a packet use the /multicast header
for the first argument and the userName for the next, then your keyword for parsing */
void OSCthulhuClient::sendChat( QString line ) {
    try {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/sC" ) << qPrintable(serverPassword)
                << userName.toStdString().c_str() << "chatLine" << line.toStdString().c_str() << osc::EndMessage;

        this->sendServer( p );
        p.Clear();
        p << osc::BeginMessage( "/chat" )
          << line.toStdString().c_str() << osc::EndMessage;
        sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to sendChat.";
    }
}

/* multicast chat received! */
void OSCthulhuClient::receiveChat( QString line ) {
    try {
        mw.addChatLine( line );
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/chat" )
            <<  line.toStdString().c_str()
             << osc::EndMessage;
        sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to receiveChat.";
    }
}

/* send the contents of the chat window to the app (for updating at login) */
void OSCthulhuClient::getChat() {
    int bufferSize;

    bufferSize = 256 * mw.getChatText().size();

    try {
        char buffer[bufferSize];
        osc::OutboundPacketStream p( buffer, bufferSize );
        p << osc::BeginMessage( "/getChat" )
            <<  mw.getChatText().toStdString().c_str()
             << osc::EndMessage;
        sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to receiveChat.";
    }
}

void  OSCthulhuClient::login() {
    try {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/login" )
            << this->serverPassword.toLatin1().constData()
            << this->userName.toLatin1().constData()
            << this->userPassword.toLatin1().constData()
            << (float)clientPort
            << osc::EndMessage;

        this->sendServer(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to login.";
    }
}

void  OSCthulhuClient::logout() {
    try {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/logout" )
                   << this->serverPassword.toLatin1().constData() << this->userName.toLatin1().constData() << this->userPassword.toLatin1().constData() << (float)clientPort << osc::EndMessage;

        this->sendServer(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to logout.";
    }
}

QString OSCthulhuClient::getUserName() {
    return userName;
}
int OSCthulhuClient::getClientPort() {
    return clientPort;
}
int OSCthulhuClient::getServerPort() {
    return serverPort;
}

//=========================================
//Peers
//=========================================
bool OSCthulhuClient::containsPeer(QString userName) {
    QReadLocker locker(&lock);
    return peers.contains(userName);
}
void OSCthulhuClient::addPeer(QString userName) {
    QWriteLocker locker(&lock);
    peers.insert(userName,new ClientUser(userName));
    emit peerAdded(userName);
}
void OSCthulhuClient::removePeer(QString userName) {
    QWriteLocker locker(&lock);

    try{
        if(peers.contains(userName)) {
            if(peers.value(userName)) {
                delete peers.value(userName);
            }
            peers.remove(userName);
            emit peerRemoved(userName);
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to remove a peer.";
    }
}

void OSCthulhuClient::printUsers() {
    QReadLocker locker(&lock);
    qDebug() << "Current Peers: ";
    QHashIterator<QString, QPointer<ClientUser> > i(peers);
    try{
        while (i.hasNext()) {
             i.next();
            qDebug() << qPrintable(i.key());
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to remove a print users.";
    }
}
void OSCthulhuClient::handleQuit() {
    logout();
    logout();
    logout();
    this->oscThread->quitConnection();
    qDebug() << "Client is shutting down...";
    wait4CleanupDone->release();
}

//=========================================
//SyncObjects
//=========================================
bool OSCthulhuClient::addSyncObject(QString name, QString type, QString subtype) {
    QWriteLocker locker(&syncObjectLock);
    try{
        if(!this->syncObjects.contains(name)) {
            this->syncObjects.insert(name,new SyncObject(name,type,subtype));

            if(objectPrinting)
            {
                buildObjectString(name, syncObjects.value(name));
            }

            return true;
        } else {
            return false;
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to remove add a sync Object.";
        return false;
    }
}
void OSCthulhuClient::addSyncArg(QString name, QVariant* argValue) {
    QWriteLocker locker(&syncObjectLock);

    try{
        if(this->syncObjects.value(name)) {
            this->syncObjects.value(name)->addArgument(argValue);

            if(objectPrinting)
            {
                buildObjectString(name, syncObjects.value(name));
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to add a syncArg to a syncObject.";
    }
}
void OSCthulhuClient::setSyncArg(QString name, int argNum, QVariant* argValue) {
    QWriteLocker locker(&syncObjectLock);
    try{
        if(this->syncObjects.contains(name)) {
            if(this->syncObjects.value(name)) {
                if(syncObjects.value(name)->getInitialized()) {
                    this->syncObjects.value(name)->setArgument(argNum,argValue);
                    emit syncArgChanged(name,argNum);

                    if(objectPrinting)
                    {
                        buildObjectString(name, syncObjects.value(name));
                    }
                }
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to set a syncArg.";
    }
}

void OSCthulhuClient::localSetSyncArg(QString name, int argNum, QVariant* argValue, int blockedPort) {
    QWriteLocker locker(&syncObjectLock);
    if(this->syncObjects.contains(name)) {
        if(this->syncObjects.value(name)) {
            this->syncObjects.value(name)->setArgument(argNum,argValue);
            emit localSyncArgChanged(name,argNum,blockedPort);

            if(objectPrinting)
            {
                buildObjectString(name, syncObjects.value(name));
            }
        }
    }
}

void OSCthulhuClient::removeSyncObject(QString name) {
    QWriteLocker locker(&syncObjectLock);
    try {
        if(this->syncObjects.contains(name)) {
            if(syncObjects.value(name)->getInitialized()) {
                qDebug() << "Removing Sync Object " << name;
                QString type=syncObjects.value(name)->getType();
                QString subType=syncObjects.value(name)->getSubtype();
                emit syncObjectRemoved(name,type,subType);
                if(syncObjects.value(name)) {
                    delete this->syncObjects.value(name);
                    if(objectPrinting)
                    {
                        removePrintObject(name);
                    }
                }
                this->syncObjects.remove(name);
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to remove a syncObject.";
    }
}

void OSCthulhuClient::finalizeSyncObject(QString name) {
    QWriteLocker locker(&syncObjectLock);
    try{
        if(this->syncObjects.contains(name)) {
            if(this->syncObjects.value(name)) {
                syncObjects.value(name)->finishInitializtion();

                if(objectPrinting)
                {
                    buildObjectString(name, syncObjects.value(name));
                }

                emit syncObjectAdded(name);
            }
        }
        qDebug() << "Client contains " << syncObjects.size() << "SyncObjects.";
    } catch(...) {
        qCritical() << "An error occurred while attempting to finalize a syncObject.";
    }
}



//=========================================
//State Sync
//=========================================
void OSCthulhuClient::newStateSync() {
    QWriteLocker locker(&tempSyncObjectLock);
    try{
        QHashIterator<QString, QPointer<SyncObject> > i(this->tempSyncObjects);
        while (i.hasNext()) {
            i.next();
            if(i.value()) {
                delete i.value();
            }
        }
        this->tempSyncObjects.clear();
    } catch(...) {
        qCritical() << "An error occurred while attempting to start a newStateSync.";
    }
}

bool OSCthulhuClient::addTempSyncObject(QString name, QString type, QString subtype) {
    QWriteLocker locker(&tempSyncObjectLock);
    try{
        if(!this->tempSyncObjects.contains(name)) {
            this->tempSyncObjects.insert(name,new SyncObject(name,type,subtype));
            return true;
        } else {
            return false;
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to add a TempSyncObject.";
    }
}

void OSCthulhuClient::addTempSyncArg(QString name,QVariant* argValue) {
    QWriteLocker locker(&tempSyncObjectLock);
    try{
        if(this->tempSyncObjects.contains(name)) {
            if(this->tempSyncObjects.value(name)) {
                this->tempSyncObjects.value(name)->addArgument(argValue);
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to add a tempSyncArg.";
    }
}
void OSCthulhuClient::finalizeStateSync() {
    QWriteLocker locker(&syncObjectLock);
    QWriteLocker locker2(&tempSyncObjectLock);
    //Discard objects not on server
    try{
        QHashIterator<QString, QPointer<SyncObject> > i(this->syncObjects);
        while (i.hasNext()) {
            i.next();
            if(!this->tempSyncObjects.contains(i.key())) {
                removeSyncObject(i.key());
            }
        }

        //Add objects not on client
        QHashIterator<QString, QPointer<SyncObject> > i2(this->tempSyncObjects);
        while (i2.hasNext()) {
            i2.next();
            if(!this->syncObjects.contains(i2.key())) {
                qDebug() << "Adding object " << i2.key();
                this->syncObjects.insert(i2.key(),i2.value());
                this->syncObjects.value(i2.key())->finishInitializtion();
                emit syncObjectAdded(i2.key());
            } else {
                if(i2.value()) {
                    //Synchronize Arg Values
                    QListIterator<QVariant* > iarg(i2.value().data()->getArgs());
                    int count=0;
                    while (iarg.hasNext()) {
                        QVariant* arg = iarg.next();
                        this->syncObjects.value(i2.key()).data()->setArgument(count, arg);
                        emit syncArgChanged(i2.key(),count);
                        count++;
                    }
                    delete i2.value();
                }
            }
        }
        this->tempSyncObjects.clear();
    } catch(...) {
        qCritical() << "An error occurred while attempting to finalize a stateSync.";
    }
}

//======================
//External App Sending
//======================
void OSCthulhuClient::appLogin() {
    QReadLocker locker(&lock);
    QReadLocker locker2(&syncObjectLock);

    try {
        QHashIterator<QString, QPointer<ClientUser> > i(peers);
        while (i.hasNext()) {
            i.next();
            sendAddPeer(i.key());
        }

        QHashIterator<QString, QPointer<SyncObject> > iso(syncObjects);
        while (iso.hasNext()) {
            iso.next();
            this->sendSyncObject(iso.key());
        }

        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/userName" )
            << qPrintable(userName)
            << osc::EndMessage;
        this->sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to send an app login info.";
    }
}

void OSCthulhuClient::sendAddPeer(QString userName) {
    try{
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/addPeer" )
            << qPrintable(userName)
            << osc::EndMessage;
        this->sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to sendAddPeer.";
    }
}
void OSCthulhuClient::sendRemovePeer(QString userName) {
    try {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/removePeer" )
            << qPrintable(userName)
            << osc::EndMessage;
        this->sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to send remove peer.";
    }
}

void OSCthulhuClient::sendRemoveSyncObject(QString name,QString type, QString subType) {
    try {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/removeSyncObject" )
            << qPrintable(name)
            << qPrintable(type)
            << qPrintable(subType)
            << osc::EndMessage;
        this->sendToApp(p);
    } catch(...) {
        qCritical() << "An error occurred while attempting to send remove sync Object.";
    }
}

void OSCthulhuClient::flush() {
    try {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        foreach(QPointer<SyncObject> object,syncObjects) {
            p << osc::BeginMessage( "/rO" )
                << qPrintable(serverPassword)
                << qPrintable(object->getName())
                << osc::EndMessage;
            this->sendServer(p);
            p.Clear();
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to flush.";
    }
}

void OSCthulhuClient::setObjectPrinting(bool printing)
{
    objectPrinting = printing;
}

void OSCthulhuClient::setOSCPrinting(bool printing)
{
    oscPrinting = printing;
}

void OSCthulhuClient::sendSyncObject(QString name) {
    try {
        //QReadLocker locker(&syncObjectLock);
        if(syncObjects.value(name)) {
            if(syncObjects.value(name)->getInitialized()) {
                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/addSyncObject" )
                    << qPrintable(name)
                    << qPrintable(syncObjects.value(name)->getType())
                    << qPrintable(syncObjects.value(name)->getSubtype());

                QListIterator<QVariant*> ia(syncObjects.value(name)->getArgs());
                while (ia.hasNext()) {
                    QVariant* arg = ia.next();
                    //p << qPrintable(ia.key());
                    if(arg->type() == QVariant::Int) {
                        p << arg->toInt();
                    } else if(arg->type() == QVariant::Double) {
                        p << (float)arg->toDouble();
                    } else if(arg->type() == QVariant::String) {
                        p << qPrintable(arg->toString());
                    } else {
                        p << (float)arg->toDouble();
                    }
                }
                p << osc::EndMessage;
                this->sendToApp(p);
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to send sync object.";
    }
}

void OSCthulhuClient::sendSyncArg(QString name, int argNum) {
    try {
        //QReadLocker locker(&syncObjectLock);
        if(this->syncObjects.value(name)) {
            if(syncObjects.value(name)->getInitialized()) {
                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/setSyncArg" )
                    << qPrintable(name)
                    << argNum;
                if(syncObjects.value(name)->getArgument(argNum)->type()== QVariant::Int) {
                    p << syncObjects.value(name)->getArgument(argNum)->toInt();
                } else if(syncObjects.value(name)->getArgument(argNum)->type()== QVariant::Double) {
                    p << (float)syncObjects.value(name)->getArgument(argNum)->toDouble();
                } else if(syncObjects.value(name)->getArgument(argNum)->type()== QVariant::String) {
                    p << qPrintable(syncObjects.value(name)->getArgument(argNum)->toString());
                } else {
                    p << (float)(syncObjects.value(name)->getArgument(argNum)->toDouble());
                }
                    p<< qPrintable(syncObjects.value(name)->getType());
                    p<< qPrintable(syncObjects.value(name)->getSubtype());
                p << osc::EndMessage;
                this->sendToApp(p);
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to send syncarg.";
    }
}

void OSCthulhuClient::localSendSyncArg(QString name, int argNum, int blockedPort) {
    try {
        //QReadLocker locker(&syncObjectLock);
        if(this->syncObjects.value(name)) {
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/setSyncArg" )
            << qPrintable(name)
            << argNum;
        if(syncObjects.value(name)->getArgument(argNum)->type()== QVariant::Int) {
            p << syncObjects.value(name)->getArgument(argNum)->toInt();
        } else if(syncObjects.value(name)->getArgument(argNum)->type()== QVariant::Double) {
            p << (float)syncObjects.value(name)->getArgument(argNum)->toDouble();
        } else if(syncObjects.value(name)->getArgument(argNum)->type()== QVariant::String) {
            p << qPrintable(syncObjects.value(name)->getArgument(argNum)->toString());
        } else {
            p << (float)(syncObjects.value(name)->getArgument(argNum)->toDouble());
        }
            p<< qPrintable(syncObjects.value(name)->getType());
            p<< qPrintable(syncObjects.value(name)->getSubtype());
        p << osc::EndMessage;
        this->localSendToApp(p,blockedPort);
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to send localsyncarg.";
    }
}

void OSCthulhuClient::sendToApp(osc::OutboundPacketStream p) {
    try {
        for(int i=0;i<appPorts.size();i++) {
            oscThread->send("127.0.0.1",appPorts.at(i),p);
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to sendToApp.";
    }
}

void OSCthulhuClient::localSendToApp(osc::OutboundPacketStream p, int blockedPort) {
    for(int i=0;i<appPorts.size();i++) {
        if(appPorts.at(i)!=blockedPort) {
            oscThread->send("127.0.0.1",appPorts.at(i),p);
        }

    }
}

void OSCthulhuClient::changeAppPorts(QList<int> appPorts)
{
    try {
        this->appPorts.clear();
        this->appPorts = appPorts;
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/ports" );
        foreach (int port, appPorts) {
            p << port;
        }
        p << osc::EndMessage;
        sendToApp(p);

        /*
        Old Port list code
        mw.getUI()->PortList->clear();
        int i=0;
        foreach (int port, appPorts) {
            mw.getUI()->PortList->insertItem(i,QString().setNum(port));
            i++;
        }
        */
        mw.resetPortList();
        for(int i = 0; i < appPorts.size(); i++)
        {
            mw.addPort(appPorts.at(i));
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to send changeAppPorts.";
    }
}

void OSCthulhuClient::printMessage(osc::ReceivedMessage message)
{
    try {
        if(oscPrinting)
        {
            QString messageString;
            messageString.append(message.AddressPattern());

            osc::ReceivedMessage::const_iterator arg = message.ArgumentsBegin();
            while(arg != message.ArgumentsEnd()) {

                messageString.append(" ");

                if((arg)->IsFloat())
                {
                    messageString.append(QString::number((arg)->AsFloat(), 'f'));
                }

                else if((arg)->IsDouble())
                {
                    messageString.append(QString::number((arg)->AsDouble(), 'f'));
                }

                else if((arg)->IsString())
                {
                    messageString.append((arg)->AsString());
                }

                arg++;
            }

            messageString.append(" ");

            emit printOSC(messageString);
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to print osc.";
    }
}

QString OSCthulhuClient::getServerPassword() {
    return serverPassword;
}
QString OSCthulhuClient::getUserPassword() {
    return userPassword;
}
void OSCthulhuClient::connectedToServer() {
    try {
        this->connected = true;
        loginThread->connectedToServer();
        aliveTimer = 0;
        char buffer[OUTPUT_BUFFER_SIZE];
        osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
        p << osc::BeginMessage( "/connected" );
        p << osc::EndMessage;
        sendToApp(p);
        emit connectedGUI();
    } catch(...) {
        qCritical() << "An error occurred while attempting to connect to server.";
        this->connected = false;
    }
}

void OSCthulhuClient::buildObjectString(QString name, QPointer<SyncObject> syncObject)
{
    try {
        if(syncObject) {
            QList<QVariant*> variants = syncObject.data()->getArgs();
            QStringList arguments;
            for(int i = 0; i < variants.size(); i++)
            {
                arguments.append(variants.at(i)->toString());
            }

            emit printObject(name, arguments);
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to build object string for printing.";
    }
}

void OSCthulhuClient::resetAliveTimer()
{
    aliveTimer = 0;
}

void OSCthulhuClient::increaseAliveTimer()
{
    try {
        if(connected) {
            aliveTimer+=1;
            if(aliveTimer > 15) {
                connected = false;
                loginThread->restart();
                emit disconnectedGUI();
                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/disconnected" );
                p << osc::EndMessage;
                sendToApp(p);
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to increase alive timer.";
    }
}

void OSCthulhuClient::changeUserPiece(QString user, QString piece)
{
    QWriteLocker locker(&lock);
    try {
        if(peers.contains(user)) {
            if(peers.value(user)) {
                peers.value(user).data()->changePiece(piece);
                emit changeGUIPiece(user,piece);
            }
        }
    } catch(...) {
        qCritical() << "An error occurred while attempting to change user Piece.";
    }
}

void OSCthulhuClient::setPreferences(QString userName,QString userPassword,QString serverAddress,QString serverPassword,int serverPort,int clientPort,int appPort)
{
    logout();
    logout();
    logout();

    peers.remove(this->userName);
    emit peerRemoved(this->userName);

    this->userName = userName;
    this->userPassword = userPassword;
    this->serverAddress = serverAddress;
    this->serverPassword = serverPassword;
    this->serverPort = serverPort;
    this->clientPort = clientPort;
    this->appPort = appPort;
    if(!appPorts.contains(appPort)) {
        appPorts.append(appPort);
    }

    mw.setUserName( userName );

    settings->setValue("userName",QVariant(userName));
    settings->setValue("userPassword",QVariant(userPassword));
    settings->setValue("serverAddress",QVariant(serverAddress));
    settings->setValue("serverPassword",QVariant(serverPassword));
    settings->setValue("serverPort",QVariant(serverPort));
    settings->setValue("clientPort",QVariant(clientPort));
    settings->setValue("appPort",QVariant(appPort));

    aliveTimer = 16;
    increaseAliveTimer();
}
